// PocketPasswordGen.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "PocketPasswordGen.h"
#include "resourceppc.h"

#include <wincrypt.h>

#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))

typedef struct {
	TCHAR ch;
	LPCTSTR phonetic;
} PhoneticLetter;

static PhoneticLetter gbl_lowerCase[] =
{
	{'a', _T("Alpha")},
	{'b', _T("Bravo")},
	{'c', _T("Charlie")},
	{'d', _T("Delta")},
	{'e', _T("Echo")},
	{'f', _T("Foxtrot")},
	{'g', _T("Golf")},
	{'h', _T("Hotel")},
	{'i', _T("India")},
	{'j', _T("Juliet")},
	{'k', _T("Kilo")},
	{'l', _T("Lima")},
	{'m', _T("Mike")},
	{'n', _T("November")},
	{'o', _T("Oscar")},
	{'p', _T("Papa")},
	{'q', _T("Quebec")},
	{'r', _T("Romeo")},
	{'s', _T("Sierra")},
	{'t', _T("Tango")},
	{'u', _T("Uniform")},
	{'v', _T("Victor")},
	{'w', _T("Whiskey")},
	{'x', _T("X-ray")},
	{'y', _T("Yankee")},
	{'z', _T("Zulu")}
};

static PhoneticLetter gbl_upperCase[] =
{
	{'A', _T("ALPHA")},
	{'B', _T("BRAVO")},
	{'C', _T("CHARLIE")},
	{'D', _T("DELTA")},
	{'E', _T("ECHO")},
	{'F', _T("FOXTROT")},
	{'G', _T("GOLF")},
	{'H', _T("HOTEL")},
	{'I', _T("INDIA")},
	{'J', _T("JULIET")},
	{'K', _T("KILO")},
	{'L', _T("LIMA")},
	{'M', _T("MIKE")},
	{'N', _T("NOVEMBER")},
	{'O', _T("OSCAR")},
	{'P', _T("PAPA")},
	{'Q', _T("QUEBEC")},
	{'R', _T("ROMEO")},
	{'S', _T("SIERRA")},
	{'T', _T("TANGO")},
	{'U', _T("UNIFORM")},
	{'V', _T("VICTOR")},
	{'W', _T("WHISKEY")},
	{'X', _T("X-RAY")},
	{'Y', _T("YANKEE")},
	{'Z', _T("ZULU")}
};

static PhoneticLetter gbl_digits[] =
{
	{'0', _T("Zero")},
	{'1', _T("One")},
	{'2', _T("Two")},
	{'3', _T("Three")},
	{'4', _T("Four")},
	{'5', _T("Five")},
	{'6', _T("Six")},
	{'7', _T("Seven")},
	{'8', _T("Eight")},
	{'9', _T("Nine")}
};

static PhoneticLetter gbl_puncuation[] =
{
	{'~', _T("Tilda")},
	{'!', _T("Exclamation Mark")},
	{'@', _T("At Symbol")},
	{'#', _T("Pound Sign")},
	{'$', _T("Dollar Sign")},
	{'%', _T("Percent")},
	{'^', _T("Carrot Symbol")},
	{'&', _T("Ampersand")},
	{'*', _T("Asterisk")},
	{'(', _T("Left Parenthesis")},
	{')', _T("Right Parenthesis")},
	{'-', _T("Minus Sign")},
	{'+', _T("Plus Sign")},
	{'=', _T("Equals Sign")},
	{'\'', _T("Apostrophe")},
	{'\"', _T("Quote")},
	{',', _T("Comma")},
	{'.', _T("Period")},
	{'?', _T("Question Mark")}
};

// Find a character in a PhoneticLetter table and return (in the pszPhonetic buffer)
// the matching phonetic letter. Returns TRUE if a match was found, otherwise FALSE.
static BOOL FindLetter(PhoneticLetter * pList, int length, WCHAR letter, WCHAR * pszPhonetic)
{
	for (int i = 0; i < length; i++)
	{
		if (pList[i].ch == letter)
		{
			_tcscpy(pszPhonetic, pList[i].phonetic);
			return TRUE;
		}
	}

	return FALSE;
}

// Append all the letters with a PhoneticLetter lookup table onto the
// end the 'pszData' string.
static void AddLetters(WCHAR * pszData, PhoneticLetter * pList, int length)
{
	pszData += _tcslen(pszData);

	for (int i = 0; i < length; i++)
	{
		*pszData++ = pList[i].ch;
	}
	
	*pszData = '\0';
}

// Generate a password out of a random sequence of characters. Store
// the 'len' character length password in the pszPassword buffer
// provided by the caller.
static void GeneratePassword(WCHAR *pszPassword, int len,
							 BOOL bIncludeLowerCase, BOOL bIncludeUpperCase,
							 BOOL bIncludeNumbers, BOOL bIncludePuncation)
{
	// Generate a list of possible password characters
	WCHAR szOptions[128] = {0};

	if (bIncludeLowerCase)
		AddLetters(szOptions, gbl_lowerCase, ARRAY_SIZE(gbl_lowerCase));

	if (bIncludeUpperCase)
		AddLetters(szOptions, gbl_upperCase, ARRAY_SIZE(gbl_upperCase));

	if (bIncludeNumbers)
		AddLetters(szOptions, gbl_digits, ARRAY_SIZE(gbl_digits));

	if (bIncludePuncation)
		AddLetters(szOptions, gbl_puncuation, ARRAY_SIZE(gbl_puncuation));

	// Use the Cryptographic API to obtain a cryptographic
	// strength random number to use as the seed for our
	// random number generator.
	DWORD dwRandomSeed;
	HCRYPTPROV hCrypt;
	
	if (CryptAcquireContext(&hCrypt, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
	{
		if (CryptGenRandom(hCrypt, sizeof(dwRandomSeed), (BYTE*)&dwRandomSeed))
		{
			srand(dwRandomSeed);
		}

		CryptReleaseContext(hCrypt, 0);
	}

	// Randomly select enough characters from the list
	// of options until we have a password of the
	// desired length.
	for (int i = 0; i < len; i++)
	{
		*pszPassword++ = szOptions[rand() % _tcslen(szOptions)];			
	}

	// NULL terminate the password string
	*pszPassword = '\0';
}

// Convert the password 'pszPassword' into its phonetic form within
// the 'pszPhonetic' buffer.
static void ConvertToPhonetic(WCHAR * pszPassword,  WCHAR * pszPhonetic)
{
	#define LOOKUP_TABLE(table, character) \
		FindLetter(table, ARRAY_SIZE(table), character, szTemp)

	int len = _tcslen(pszPassword);

	// Start off with an empty string
	pszPhonetic[0] = '\0';

	for (int i = 0; i < len; i++)
	{
		WCHAR szTemp[32];

		// Use the lookup tables to determine the phonetic form
		// of the current password character.
		LOOKUP_TABLE(gbl_lowerCase, pszPassword[i])
			|| LOOKUP_TABLE(gbl_upperCase, pszPassword[i])
			|| LOOKUP_TABLE(gbl_digits, pszPassword[i])
			|| LOOKUP_TABLE(gbl_puncuation, pszPassword[i]);

		// and append it to the string we're building
		_tcscat(pszPhonetic, szTemp);

		// If there are more characters append a seperator
		if (i != len - 1)
		{
			_tcscat(pszPhonetic, _T(", "));
		}
	}
}

// Returns true if the checkbox is currently checked
static BOOL IsChecked(HWND hDlg, int id)
{
	HWND hWndCtrl = GetDlgItem(hDlg, id);
	return (SendMessage(hWndCtrl, BM_GETCHECK, 0, 0) == BST_CHECKED);
}

static int GetInteger(HWND hDlg, int id)
{
	// Get the text stored in the text box
	WCHAR szBuffer[64];
	GetDlgItemText(hDlg, id, szBuffer, 64);

	// Convert it into an integer
	return _wtoi(szBuffer);
}


INT_PTR CALLBACK PocketPasswordGenDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	HWND hWndCtrl;

    switch (message)
    {
        case WM_INITDIALOG:
            {
                // Create a Done button and size it.  
                SHINITDLGINFO shidi;
				SHMENUBARINFO mbi;
                shidi.dwMask = SHIDIM_FLAGS;
                shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN;
                shidi.hDlg = hDlg;
                SHInitDialog(&shidi);

				// Create a menu bar
                memset(&mbi, 0, sizeof(SHMENUBARINFO));
				mbi.cbSize     = sizeof(SHMENUBARINFO);
				mbi.hwndParent = hDlg;
				mbi.nToolBarId = IDR_MENU;
				mbi.hInstRes   = GetModuleHandle(NULL);
				mbi.dwFlags = SHCMBF_HMENU;
	            SHCreateMenuBar(&mbi);

				// Set the range and default value of the spinner
				hWndCtrl = GetDlgItem(hDlg, IDC_SPIN_LENGTH);
				SendMessage(hWndCtrl, UDM_SETRANGE32, 6, 24);
				SendMessage(hWndCtrl, UDM_SETPOS, 0, MAKELPARAM(8, 0));

				// Change the Password static control to use a bold font
				LOGFONT lf;
				memset(&lf, 0, sizeof(LOGFONT));
				HDC hdc = GetDC(NULL);
				lf.lfHeight = -9 * GetDeviceCaps(hdc, LOGPIXELSY) / 72;
				ReleaseDC(NULL, hdc);
				lf.lfWeight = FW_BOLD;
				HFONT hFont = CreateFontIndirect(&lf);

				SendDlgItemMessage(hDlg, IDC_LABEL_PASSWORD, WM_SETFONT, (WPARAM)hFont, 0);
            }
            return (INT_PTR)TRUE;

		case WM_CTLCOLORSTATIC:
			{
				// Change the background colour of labels
				HBRUSH brush = CreateSolidBrush(RGB(240, 240, 240));
				SetBkMode((HDC)wParam, TRANSPARENT);
				return (INT_PTR)brush;
			}
			break;

		case WM_PAINT:
			{
				HBRUSH hbrBackground;
				HPEN hOldPen, hPen;
				PAINTSTRUCT ps;
				RECT rcBounds;

				HDC hDC = BeginPaint(hDlg, &ps);
				GetClientRect(hDlg, &rcBounds);

				// Fill in the background
				hbrBackground = CreateSolidBrush(RGB(240, 240, 240));
				FillRect(hDC, &rcBounds, hbrBackground);
				DeleteObject(hbrBackground);

				// Draw the dividing line(s)
				hPen = CreatePen(PS_SOLID, 1, RGB(213, 223, 229));
				hOldPen = (HPEN)SelectObject(hDC, hPen);
				MoveToEx(hDC, 3, 137, NULL);
				LineTo(hDC, 234, 137);

				SelectObject(hDC, GetStockObject(WHITE_PEN));
				MoveToEx(hDC, 3, 138, NULL);
				LineTo(hDC, 234, 138);

				SelectObject(hDC, hOldPen);
				DeleteObject(hPen);
				
				EndPaint(hDlg, &ps);
			}
			break;

        case WM_COMMAND:
            switch (LOWORD(wParam))
			{
				case IDOK:
					EndDialog(hDlg, LOWORD(wParam));
					return (INT_PTR)TRUE;

				case IDC_GENERATE:
					{
						WCHAR szPassword[2048];

						// Query the UI controls to determine the parameters
						// that control the password generation process
						int len = GetInteger(hDlg, IDC_EDIT_LENGTH);
						BOOL bShowPhonetics    = IsChecked(hDlg, IDC_CHECK_PHONETICS);
						BOOL bIncludeLowerCase = IsChecked(hDlg, IDC_CHECK_LOWERCASE);
						BOOL bIncludeUpperCase = IsChecked(hDlg, IDC_CHECK_UPPERCASE);
						BOOL bIncludeNumbers   = IsChecked(hDlg, IDC_CHECK_NUMBERS);
						BOOL bIncludePuncation = IsChecked(hDlg, IDC_CHECK_PUNCTUATION);

						if (!bIncludeLowerCase && !bIncludeUpperCase && !bIncludeNumbers && !bIncludePuncation)
						{
							MessageBox(hDlg, L"You must select atleast one of the character range checkboxes.", L"Error", MB_ICONEXCLAMATION | MB_OK);
							return (INT_PTR)TRUE;
						}

						// Generate a new random password
						GeneratePassword(szPassword, len,
							bIncludeLowerCase, bIncludeUpperCase,
							bIncludeNumbers, bIncludePuncation);

						// Place the password into the first textbox
						// and give keyboard focus to it
						hWndCtrl = GetDlgItem(hDlg, IDC_EDIT_PASSWORD);
						SetWindowText(hWndCtrl, szPassword);
						SetFocus(hWndCtrl);
						SendMessage(hWndCtrl, EM_SETSEL, 0, -1);

						// If the show phonetics option is enabled then
						// convert the password into the phonetic alphabet
						// and display it in the second textbox
						if (bShowPhonetics)
						{
							WCHAR szPhonetic[2048];
							ConvertToPhonetic(szPassword, szPhonetic);
							SetDlgItemText(hDlg, IDC_EDIT_PHONETIC_PASSWORD, szPhonetic);
						}
						else
						{
							SetDlgItemText(hDlg, IDC_EDIT_PHONETIC_PASSWORD, _T(""));
						}
					}
					return (INT_PTR)TRUE;

				case IDC_COPY:
					{
						// Copy password to clipboard
						TCHAR szBuffer[2048];
						HLOCAL hMem = NULL;

						GetDlgItemText(hDlg, IDC_EDIT_PASSWORD, szBuffer, ARRAY_SIZE(szBuffer));
						
						if (OpenClipboard(NULL))
						{
							EmptyClipboard();
							
							hMem = LocalAlloc(LMEM_MOVEABLE,  (_tcslen(szBuffer) + 1) * sizeof(TCHAR)); 
							if (hMem != NULL) 
							{ 
								LPTSTR pszMem = (LPTSTR)LocalLock(hMem); 
								_tcscpy(pszMem, szBuffer); 
								LocalUnlock(hMem); 
								
								SetClipboardData(CF_UNICODETEXT, hMem);
							}

							CloseClipboard();
						}
					}
					return (INT_PTR)TRUE;
            }
            break;

        case WM_CLOSE:
            EndDialog(hDlg, message);
            return (INT_PTR)TRUE;

    }
    return (INT_PTR)FALSE;
}

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPTSTR    lpCmdLine,
                   int       nCmdShow)
{
	SHInitExtraControls();

	return DialogBox(hInstance, (LPCTSTR)IDD_POCKETPASSWORDGEN, NULL, PocketPasswordGenDialog);
}